import 'dart:core';
import 'dart:math';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';

import '../../../../utils/ex/list_ex.dart';
import '../../base/base_chart.dart';
import '../base/axis_entity.dart';
import '../base/base_axis_chart.dart';
import '../x_axis_render.dart';
import '../y_axis_render.dart';
import 'line_entity.dart';
import '../x_axis.dart';
import '../y_axis.dart';
import '../../ex/extension.dart';

/// 折线图代理
///
/// Created by wanggaowan on 2023/9/15 13:52
class KqLineChartDelegate extends BaseAxisChartDelegate<List<LineData>> {
  /// 构建折线图
  ///
  /// [xAxis]配置x轴相关属性，[xAxisRender]则负责x轴的绘制。
  /// [yAxis]配置y轴相关属性，[yAxisRender]则负责y轴的绘制。
  /// [dataRender]负责将折线数据绘制到界面及高亮数据绘制。
  /// [gestureHandler]为手势处理器，处理图表的点击，滑动等操作。
  /// [emptyWidgetBuilder]为无数据时构建需要显示的内容。
  /// [isEmptyData]用于判断给得的数据是否为空，从而决定是否调用[emptyWidgetBuilder]绘制空数据界面。
  /// [animDuration]为动画时间，此值大于0则绘制时伴随动画
  KqLineChartDelegate(
      {LineChartDataRender? dataRender,
      XAxis? xAxis,
      YAxis? yAxis,
      XAxisRender? xAxisRender,
      YAxisRender? yAxisRender,
      super.animDuration,
      super.data,
      LineCharGestureHandler? gestureHandler,
      super.emptyWidgetBuilder,
      super.isDataEmpty})
      : super(
          dataRender: dataRender ?? LineChartDataRender(),
          xAxis: xAxis ?? XAxis(),
          yAxis: yAxis ?? YAxis(),
          xAxisRender: xAxisRender ?? XAxisRender(),
          yAxisRender: yAxisRender ?? YAxisRender(),
          gestureHandler: gestureHandler,
        );

  /// 构建折线图，对非必传的功能提供默认实现
  ///
  /// [xAxis]配置x轴相关属性，[xAxisRender]则负责x轴的绘制。
  /// [yAxis]配置y轴相关属性，[yAxisRender]则负责y轴的绘制。
  /// [dataRender]负责将折线数据绘制到界面及高亮数据绘制。
  /// [gestureHandler]为手势处理器，处理图表的点击，滑动等操作。
  /// [emptyWidgetBuilder]为无数据时构建需要显示的内容。
  /// [isEmptyData]用于判断给得的数据是否为空，从而决定是否调用[emptyWidgetBuilder]绘制空数据界面。
  /// [animDuration]为动画时间，此值大于0则绘制时伴随动画
  KqLineChartDelegate.withDefault(
      {XAxis? xAxis,
      YAxis? yAxis,
      LineChartDataRender? dataRender,
      XAxisRender? xAxisRender,
      YAxisRender? yAxisRender,
      Duration? animDuration,
      List<LineData>? data,
      LineCharGestureHandler? gestureHandler,
      Widget Function()? emptyWidgetBuilder,
      bool Function(List<LineData>? data)? isDataEmpty})
      : this(
            xAxis: xAxis,
            yAxis: yAxis,
            dataRender: dataRender,
            xAxisRender: xAxisRender,
            yAxisRender: yAxisRender,
            animDuration: animDuration,
            data: data,
            gestureHandler: gestureHandler ?? LineCharGestureHandler(),
            emptyWidgetBuilder: emptyWidgetBuilder ?? getDefEmptyView,
            isDataEmpty: isDataEmpty);

  @override
  LineCharGestureHandler? get gestureHandler =>
      super.gestureHandler as LineCharGestureHandler?;

  @override
  XAxis get xAxis => super.xAxis as XAxis;

  @override
  YAxis get yAxis => super.yAxis as YAxis;

  @override
  bool get isEmptyData {
    var isDataEmptyFunc = isDataEmpty;
    if (isDataEmptyFunc != null) {
      return isDataEmptyFunc.call(data);
    }

    if (data.isNullOrEmpty) {
      return true;
    }

    var isEmpty = true;
    for (var d in data!) {
      if (d.values.isNotEmpty) {
        isEmpty = false;
        break;
      }
    }
    return isEmpty;
  }

  @override
  void didUpdateWidget(covariant BaseChart oldWidget) {
    super.didUpdateWidget(oldWidget);
    _updateGestureHandler(oldWidget);
  }

  void _updateGestureHandler(BaseChart oldWidget) {
    var gestureHandler = this.gestureHandler;
    if (gestureHandler == null) {
      return;
    }

    gestureHandler.update(oldWidget.delegate.gestureHandler);
  }
}

class LineChartDataRender with BaseDataRenderMixin<KqLineChartDelegate> {
  LineChartDataRender({this.highLightRender});

  final LineChartHighLightRender? highLightRender;

  /// 记录绘制路径的分段数据
  final List<double> _pathSegmentedList = [];

  @override
  void onDraw(KqLineChartDelegate chart, Canvas canvas, Rect rect,
      double gridWidth, double gridHeight, double animProgress) {
    var data = chart.data;
    if (data == null || data.isEmpty) {
      return;
    }

    for (var it in data) {
      Path path;
      if (it.values.length <= 1) {
        path = Path();
        if (it.values.length == 1) {
          getEntityPosition(chart, it.values[0], rect, gridWidth);
        }
      } else if (it.lineType == LineType.cubicBezier) {
        path = createCubicBezierPath(chart, it, rect, gridWidth);
      } else if (it.lineType == LineType.horizontalBezier) {
        path = createHorizontalBezierPath(chart, it, rect, gridWidth);
      } else {
        path = createLinerPath(chart, it, rect, gridWidth);
      }

      drawLine(chart, canvas, it, rect, animProgress, path);
      drawValueTextAndIndicator(
          canvas, it, rect.top, rect.bottom, animProgress, path);
      drawAddedLine(chart, canvas, it, rect, animProgress);
      drawVerticalAddedLine(chart, canvas, it, rect, animProgress);
    }

    if (animProgress >= 1) {
      var touchData = chart.gestureHandler?.touchData;
      if (touchData != null) {
        highLightRender?.onDraw(chart, canvas, rect, touchData);
      }
    }
  }

  /// 计算[Entity]在坐标系中的坐标位置
  @protected
  void getEntityPosition(
    KqLineChartDelegate chart,
    LineEntity entity,
    Rect rect,
    double gridWidth,
  ) {
    var x = rect.left + gridWidth * (entity.xIndex + entity.xIndexOffset);

    var yAxis = chart.yAxis;
    double y;
    if (entity.isDraw) {
      y = rect.bottom -
          ((entity.value - yAxis.min) /
              (yAxis.max - yAxis.min) *
              (rect.bottom - rect.top));
    } else {
      y = rect.bottom;
    }

    entity.$drawRect = Rect.fromLTRB(x, y, x, y);
  }

  /// 绘制折线
  @protected
  void drawLine(KqLineChartDelegate chart, Canvas canvas, LineData lineData,
      Rect rect, double animProgress, Path path) {
    if (_pathSegmentedList.isEmpty || _pathSegmentedList.length % 3 != 0) {
      return;
    }

    ui.PathMetric? pathMetric = path.computeMetric();
    if (pathMetric == null) {
      return;
    }

    var paint = Paint();
    paint.color = lineData.lineColors[0];
    paint.strokeWidth = lineData.lineWidth;
    paint.style = PaintingStyle.stroke;
    if (lineData.lineColors.length > 1) {
      paint.shader =
          LinearGradient(colors: lineData.lineColors).createShader(rect);
    }

    if (!lineData.lineDrawCouldBeyondAxis) {
      canvas.save();
      canvas.clipRect(Rect.fromLTRB(rect.left,
          rect.top - chart.yAxis.endPadding, rect.right, rect.bottom));
    }

    for (int index = 0; index < _pathSegmentedList.length ~/ 3; index++) {
      var valueIndex = _pathSegmentedList[index * 3].toInt();
      var startLength = _pathSegmentedList[index * 3 + 1];
      var endLength = _pathSegmentedList[index * 3 + 2];
      if (endLength <= startLength) {
        drawFillBg(canvas, pathMetric, startLength, endLength, lineData, rect,
            valueIndex);
        continue;
      }

      var entity = lineData.values[valueIndex];
      if (animProgress >= 1) {
        if (lineData.fillBg) {
          drawFillBg(
              canvas, pathMetric, startLength, endLength, lineData, rect);
        }

        var path = pathMetric.extractPath(startLength, endLength,
            startWithMoveTo: true);
        if (entity.lineStyle == LineStyle.dottedLine) {
          path = path.dashPath([3, 3]);
        }
        canvas.drawPath(path, paint);
        continue;
      }

      var animLength = pathMetric.length * animProgress;
      if (endLength < animLength) {
        if (lineData.fillBg) {
          drawFillBg(
              canvas, pathMetric, startLength, endLength, lineData, rect);
        }

        var path = pathMetric.extractPath(startLength, endLength,
            startWithMoveTo: true);
        if (entity.lineStyle == LineStyle.dottedLine) {
          path = path.dashPath([3, 3]);
        }
        canvas.drawPath(path, paint);
      } else if (startLength < animLength) {
        if (lineData.fillBg) {
          drawFillBg(
              canvas, pathMetric, startLength, animLength, lineData, rect);
        }

        var path = pathMetric.extractPath(startLength, animLength,
            startWithMoveTo: true);
        if (entity.lineStyle == LineStyle.dottedLine) {
          path = path.dashPath([3, 3]);
        }
        canvas.drawPath(path, paint);
      }
    }

    if (!lineData.lineDrawCouldBeyondAxis) {
      canvas.restore();
    }
  }

  /// 绘制折线背景填充色
  @protected
  void drawFillBg(Canvas canvas, ui.PathMetric pathMetric, double startLength,
      double endLength, LineData lineData, Rect rect,
      [int? index]) {
    Path tempPath;
    if (startLength == endLength) {
      // 单个点数据
      var values = lineData.values;
      if (values.isEmpty ||
          index == null ||
          index < 0 ||
          index >= values.length) {
        return;
      }
      var entity = values[index];
      double widthHalf;
      if (entity.drawIndicator && entity.indicatorSize > 0) {
        widthHalf = entity.indicatorSize / 2;
      } else {
        widthHalf = lineData.lineWidth / 2;
      }

      tempPath = Path()
        ..moveTo(entity.drawRect.left - widthHalf, entity.drawRect.top)
        ..lineTo(entity.drawRect.left + widthHalf, entity.drawRect.top)
        ..lineTo(entity.drawRect.left + widthHalf, rect.bottom)
        ..lineTo(entity.drawRect.left - widthHalf, rect.bottom)
        ..close();
    } else {
      tempPath =
          pathMetric.extractPath(startLength, endLength, startWithMoveTo: true);
      var tangentForOffset = pathMetric.getTangentForOffset(endLength);
      if (tangentForOffset == null) {
        return;
      }
      tempPath.lineTo(tangentForOffset.position.dx, rect.bottom);

      tangentForOffset = pathMetric.getTangentForOffset(startLength);
      if (tangentForOffset == null) {
        return;
      }

      tempPath.lineTo(tangentForOffset.position.dx, rect.bottom);
      tempPath.close();
    }

    if (lineData.fillBgDrawable != null) {
      canvas.save();
      canvas.clipPath(tempPath);
      canvas.drawImage(
          lineData.fillBgDrawable!, Offset(rect.left, rect.top), Paint());
      canvas.restore();
    } else if (lineData.fillBgColors.isNotEmpty) {
      if (lineData.fillBgColors.length == 1) {
        canvas.save();
        canvas.clipPath(tempPath);
        canvas.drawColor(lineData.fillBgColors[0], BlendMode.srcOver);
        canvas.restore();
      } else {
        var paint = Paint()
          ..style = PaintingStyle.fill
          ..shader = LinearGradient(
                  colors: lineData.fillBgColors,
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter)
              .createShader(rect);
        canvas.drawPath(tempPath, paint);
      }
    }
  }

  /// 创建三次贝塞尔曲线平滑绘制路径
  @protected
  Path createCubicBezierPath(KqLineChartDelegate chart, LineData lineData,
      Rect rect, double gridWidth) {
    var valuesPath = Path();
    _pathSegmentedList.clear();

    var isNewPath = true;
    LineStyle? lineStyle;
    double prevDx;
    double prevDy;
    double curDx;
    double curDy;

    LineEntity prevPrev;
    LineEntity cur = lineData.values[0];
    LineEntity prev = cur;
    LineEntity next;
    getEntityPosition(chart, lineData.values[0], rect, gridWidth);
    if (cur.isDraw) {
      lineStyle = cur.lineStyle;

      valuesPath.moveTo(cur.drawRect.left, cur.drawRect.top);
      // 开始下标
      _pathSegmentedList.add(0);
      // 开始的path length
      var length = valuesPath.computeMetric()?.length ?? 0;
      _pathSegmentedList.add(length);
      // 结束的path length，此处仅占位
      _pathSegmentedList.add(0);
      isNewPath = false;
    }

    for (var index = 1; index < lineData.values.length; index++) {
      getEntityPosition(chart, lineData.values[index], rect, gridWidth);
      prevPrev = prev;
      prev = cur;
      cur = lineData.values[index];
      var nextIndex = index + 1 < lineData.values.length ? index + 1 : index;
      next = lineData.values[nextIndex];
      if (nextIndex != index) {
        getEntityPosition(chart, lineData.values[nextIndex], rect, gridWidth);
      }

      if (cur.isDraw) {
        if (!prev.isDraw) {
          valuesPath.lineTo(cur.drawRect.left, cur.drawRect.top);
          // 重新以此点作为起始点
          prev = cur;
        } else {
          if (!next.isDraw) {
            // 下个节点不绘制，那当前节点即为结束点
            next = cur;
          }

          var cubicIntensity = lineData.cubicIntensity ?? 0.1;
          prevDx =
              (cur.drawRect.left - prevPrev.drawRect.left) * cubicIntensity;
          prevDy =
              (getDrawY(cur, rect.bottom) - getDrawY(prevPrev, rect.bottom)) *
                  cubicIntensity;
          curDx = (next.drawRect.left - prev.drawRect.left) * cubicIntensity;
          curDy = (getDrawY(next, rect.bottom) - getDrawY(prev, rect.bottom)) *
              cubicIntensity;

          valuesPath.cubicTo(
              prev.drawRect.left + prevDx,
              getDrawY(prev, rect.bottom) + prevDy,
              cur.drawRect.left - curDx,
              cur.drawRect.top - curDy,
              cur.drawRect.left,
              cur.drawRect.top);
        }

        if (lineStyle != null && lineStyle != cur.lineStyle) {
          if (_pathSegmentedList.length > 2) {
            _pathSegmentedList[_pathSegmentedList.length - 1] =
                valuesPath.computeMetric()?.length ?? 0;
          }

          isNewPath = true;
        }

        if (isNewPath) {
          // 开始下标
          _pathSegmentedList.add(index.toDouble());
          // 开始的path length
          _pathSegmentedList.add(valuesPath.computeMetric()?.length ?? 0);
          // 结束的path length，此处仅占位
          _pathSegmentedList.add(0);

          isNewPath = false;
        }

        lineStyle = cur.lineStyle;
      } else {
        if (_pathSegmentedList.length > 2 && !isNewPath) {
          // !isNewPath表示当前节点前一个节点是需要绘制的
          _pathSegmentedList[_pathSegmentedList.length - 1] =
              valuesPath.computeMetric()?.length ?? 0;
        }

        if (index < lineData.values.length - 1) {
          valuesPath.lineTo(cur.drawRect.left, cur.drawRect.top);
        }
        isNewPath = true;
      }
    }

    if (_pathSegmentedList.isNotEmpty) {
      _pathSegmentedList[_pathSegmentedList.length - 1] =
          valuesPath.computeMetric()?.length ?? 0;
    }

    return valuesPath;
  }

  @protected
  double getDrawY(AxisEntity entity, double def) {
    if (entity.isDraw) {
      return entity.drawRect.top;
    }
    return def;
  }

  /// 创建二次贝塞尔曲线平滑绘制路径
  @protected
  Path createHorizontalBezierPath(KqLineChartDelegate chart, LineData lineData,
      Rect rect, double gridWidth) {
    var valuesPath = Path();
    _pathSegmentedList.clear();

    var isNewPath = true;
    LineStyle? lineStyle;

    LineEntity prev = lineData.values[0];
    LineEntity cur = prev;
    getEntityPosition(chart, lineData.values[0], rect, gridWidth);
    if (cur.isDraw) {
      lineStyle = cur.lineStyle;

      valuesPath.moveTo(cur.drawRect.left, cur.drawRect.top);
      // 开始下标
      _pathSegmentedList.add(0);
      // 开始的path length
      _pathSegmentedList.add(valuesPath.computeMetric()?.length ?? 0);
      // 结束的path length，此处仅占位
      _pathSegmentedList.add(0);

      isNewPath = false;
    }

    for (int index = 1; index < lineData.values.length; index++) {
      getEntityPosition(chart, lineData.values[index], rect, gridWidth);
      prev = cur;
      cur = lineData.values[index];
      if (cur.isDraw) {
        if (!prev.isDraw) {
          valuesPath.lineTo(cur.drawRect.left, cur.drawRect.top);
        } else {
          var cpx =
              prev.drawRect.left + (cur.drawRect.left - prev.drawRect.left) / 2;
          valuesPath.cubicTo(
              cpx,
              getDrawY(prev, rect.bottom),
              cpx,
              getDrawY(cur, rect.bottom),
              cur.drawRect.left,
              getDrawY(cur, rect.bottom));
        }

        if (lineStyle != null && lineStyle != cur.lineStyle) {
          if (_pathSegmentedList.length > 2) {
            _pathSegmentedList[_pathSegmentedList.length - 1] =
                valuesPath.computeMetric()?.length ?? 0;
          }

          isNewPath = true;
        }

        if (isNewPath) {
          // 开始下标
          _pathSegmentedList.add(index.toDouble());
          // 开始的path length
          _pathSegmentedList.add(valuesPath.computeMetric()?.length ?? 0);
          // 结束的path length，此处仅占位
          _pathSegmentedList.add(0);

          isNewPath = false;
        }

        lineStyle = cur.lineStyle;
      } else {
        if (_pathSegmentedList.length > 2 && !isNewPath) {
          // !isNewPath表示当前节点前一个节点是需要绘制的
          _pathSegmentedList[_pathSegmentedList.length - 1] =
              valuesPath.computeMetric()?.length ?? 0;
        }

        if (index < lineData.values.length - 1) {
          valuesPath.lineTo(cur.drawRect.left, cur.drawRect.top);
        }
        isNewPath = true;
      }
    }

    if (_pathSegmentedList.isNotEmpty) {
      _pathSegmentedList[_pathSegmentedList.length - 1] =
          valuesPath.computeMetric()?.length ?? 0;
    }

    return valuesPath;
  }

  /// 创建线性的折线图路径
  @protected
  Path createLinerPath(KqLineChartDelegate chart, LineData lineData, Rect rect,
      double gridWidth) {
    var valuesPath = Path();
    _pathSegmentedList.clear();

    LineStyle? lineStyle;

    // 是否是新一段path路径
    var isNewPath = true;
    var isMoveTo = false;
    for (int index = 0; index < lineData.values.length; index++) {
      var entity = lineData.values[index];
      getEntityPosition(chart, entity, rect, gridWidth);
      if (entity.isDraw) {
        if (!isMoveTo) {
          valuesPath.moveTo(entity.drawRect.left, entity.drawRect.top);
          isMoveTo = true;
        } else {
          valuesPath.lineTo(entity.drawRect.left, entity.drawRect.top);
        }

        if (lineStyle != null && lineStyle != entity.lineStyle) {
          if (_pathSegmentedList.length > 2) {
            _pathSegmentedList[_pathSegmentedList.length - 1] =
                valuesPath.computeMetric()?.length ?? 0;
          }

          isNewPath = true;
        }

        if (isNewPath) {
          // 开始下标
          _pathSegmentedList.add(index.toDouble());
          // 开始的path length
          _pathSegmentedList.add(valuesPath.computeMetric()?.length ?? 0);
          // 结束的path length，此处仅占位
          _pathSegmentedList.add(0);

          isNewPath = false;
        }

        lineStyle = entity.lineStyle;
      } else {
        if (_pathSegmentedList.length > 2 && !isNewPath) {
          // !isNewPath表示当前节点前一个节点是需要绘制的
          _pathSegmentedList[_pathSegmentedList.length - 1] =
              valuesPath.computeMetric()?.length ?? 0;
        }

        if (!isMoveTo) {
          valuesPath.moveTo(entity.drawRect.left, entity.drawRect.top);
          isMoveTo = true;
        }

        isNewPath = true;
      }
    }

    if (_pathSegmentedList.isNotEmpty) {
      _pathSegmentedList[_pathSegmentedList.length - 1] =
          valuesPath.computeMetric()?.length ?? 0;
    }
    return valuesPath;
  }

  /// 绘制文本值和数值指示器
  @protected
  void drawValueTextAndIndicator(Canvas canvas, LineData lineData, double top,
      double bottom, double animatorProgress, Path path) {
    if (animatorProgress <= 0) {
      return;
    }

    double endLength = 0;
    if (animatorProgress < 1) {
      var pathMetric = path.computeMetric();
      if (pathMetric == null) {
        return;
      }

      var tangentForOffset =
          pathMetric.getTangentForOffset(animatorProgress * pathMetric.length);
      if (tangentForOffset != null) {
        endLength = tangentForOffset.position.dx;
      }

      if (endLength <= 0) {
        return;
      }
    }

    var textPaint = TextPainter(textDirection: TextDirection.ltr);
    for (int index = 0; index < lineData.values.length; index++) {
      var entity = lineData.values[index];
      if (entity.isDraw) {
        if (animatorProgress < 1 && endLength < entity.drawRect.left) {
          break;
        }

        var indicatorOffset = entity.labelOffset;
        if (entity.drawIndicator && entity.indicatorSize > 0) {
          var half = entity.indicatorSize / 2;
          var path = Path()
            ..addOval(Rect.fromCircle(
                center: Offset(entity.drawRect.left, entity.drawRect.top),
                radius: half));
          if (entity.indicatorDrawable != null) {
            int width = entity.indicatorDrawable!.width;
            int height = entity.indicatorDrawable!.height;
            canvas.save();
            canvas.clipPath(path);
            canvas.drawImage(
                entity.indicatorDrawable!,
                Offset(entity.drawRect.left - width / 2,
                    entity.drawRect.top - height / 2),
                Paint()
                  ..isAntiAlias = true
                  ..style = PaintingStyle.fill);
            canvas.restore();
          } else {
            canvas.drawPath(
                path,
                Paint()
                  ..isAntiAlias = true
                  ..color = entity.indicatorColor
                  ..style = PaintingStyle.fill);
          }

          indicatorOffset += half;
        }

        var label = entity.label;
        if (entity.drawLabel &&
            entity.labelTextSize > 0 &&
            label != null &&
            label.isNotEmpty) {
          var gravity = entity.labelGravity;

          textPaint.text = TextSpan(
            text: label,
            style: TextStyle(
              color: entity.labelColor,
              fontSize: entity.labelTextSize,
            ),
          );
          textPaint.layout();

          var textHeight = textPaint.height;
          var y = entity.drawRect.top - indicatorOffset - textHeight;
          if (y < top) {
            y = entity.drawRect.top + indicatorOffset;
          }

          double x = entity.drawRect.left;
          if (gravity == LabelGravity.center) {
            x -= textPaint.width / 2;
          } else if (gravity == LabelGravity.end) {
            x -= textPaint.width;
          }

          textPaint.paint(canvas, Offset(x, y));
        }
      }
    }
    textPaint.dispose();
  }

  /// 绘制附加线
  @protected
  void drawAddedLine(KqLineChartDelegate chart, Canvas canvas,
      LineData lineData, Rect rect, double animProgress) {
    var addedLines = lineData.addedLines;
    if (addedLines == null || addedLines.isEmpty) {
      return;
    }

    var tempPath = Path();
    var textPaint = TextPainter(textDirection: TextDirection.ltr);
    var paint = Paint()..style = PaintingStyle.stroke;

    var yAxis = chart.yAxis;
    var xAxis = chart.xAxis;
    canvas.save();
    var tempRect = Rect.fromLTRB(rect.left, rect.top - chart.yAxis.endPadding,
        rect.right + xAxis.endPadding, rect.bottom);
    canvas.clipRect(tempRect);

    for (var addedLine in addedLines) {
      var y = rect.bottom -
          ((addedLine.value - yAxis.min) /
              (yAxis.max - yAxis.min) *
              (rect.bottom - rect.top));
      var stopX =
          addedLine.useEndPadding ? rect.right : rect.right + xAxis.endPadding;
      var startX = addedLine.useStartPadding
          ? rect.left + xAxis.startPadding
          : rect.left;
      paint
        ..color = addedLine.lineColor
        ..strokeWidth = addedLine.lineWidth;
      tempPath.reset();
      tempPath.moveTo(startX, y);
      tempPath.lineTo(stopX * animProgress, y);
      if (addedLine.lineDash != null) {
        tempPath = tempPath.dashPath(addedLine.lineDash!);
      }
      canvas.drawPath(tempPath, paint);

      var label = addedLine.label;
      if (animProgress < 1 ||
          label == null ||
          label.isEmpty ||
          addedLine.labelTextSize <= 0) {
        continue;
      }

      textPaint.text = TextSpan(
          text: label,
          style: TextStyle(
              color: addedLine.labelTextColor,
              fontSize: addedLine.labelTextSize,
              overflow: addedLine.textOverflow));

      textPaint.ellipsis =
          xAxis.textOverflow == TextOverflow.ellipsis ? kEllipsis : null;
      textPaint.textAlign = addedLine.labelAlignment.x == 0
          ? TextAlign.center
          : addedLine.labelAlignment.x < 0
              ? TextAlign.left
              : TextAlign.right;

      var textMaxWidth = stopX - startX;
      textPaint.layout(minWidth: textMaxWidth, maxWidth: textMaxWidth);
      var textHeight = textPaint.height;
      double offsetX;
      if (addedLine.labelAlignment.x > 0) {
        offsetX = startX - textMaxWidth / 2 * (1 - addedLine.labelAlignment.x);
      } else if (addedLine.labelAlignment.x < 0) {
        offsetX = startX + textMaxWidth / 2 * (addedLine.labelAlignment.x + 1);
      } else {
        offsetX = startX;
      }

      double offsetY =
          y - textHeight / 2 + addedLine.labelAlignment.y * textHeight / 2;

      textPaint.paint(
        canvas,
        Offset(offsetX, offsetY),
      );
    }

    canvas.restore();
    textPaint.dispose();
  }

  /// 绘制附加线
  @protected
  void drawVerticalAddedLine(KqLineChartDelegate chart, Canvas canvas,
      LineData lineData, Rect rect, double animProgress) {
    var addedLines = lineData.verticalAddedLines;
    if (addedLines == null || addedLines.isEmpty) {
      return;
    }

    var tempPath = Path();
    var textPaint = TextPainter(textDirection: TextDirection.ltr);
    var paint = Paint()..style = PaintingStyle.stroke;

    var yAxis = chart.yAxis;
    var xAxis = chart.xAxis;
    canvas.save();
    var tempRect = Rect.fromLTRB(rect.left, rect.top - chart.yAxis.endPadding,
        rect.right + xAxis.endPadding, rect.bottom);
    canvas.clipRect(tempRect);

    for (var addedLine in addedLines) {
      var percent = (addedLine.value) / (xAxis.labelCount - 1);
      if (percent > animProgress) {
        continue;
      }

      var x = rect.left + (percent * (rect.right - rect.left));
      var stopY =
          addedLine.useEndPadding ? rect.top : rect.top - yAxis.endPadding;
      var startY = addedLine.useStartPadding
          ? rect.bottom - yAxis.startPadding
          : rect.bottom;
      paint
        ..color = addedLine.lineColor
        ..strokeWidth = addedLine.lineWidth;
      tempPath.reset();
      tempPath.moveTo(x, startY);
      tempPath.lineTo(x, stopY);
      if (addedLine.lineDash != null) {
        tempPath = tempPath.dashPath(addedLine.lineDash!);
      }
      canvas.drawPath(tempPath, paint);

      var label = addedLine.label;
      if (label == null || label.isEmpty || addedLine.labelTextSize <= 0) {
        continue;
      }

      textPaint.text = TextSpan(
          text: label,
          style: TextStyle(
              color: addedLine.labelTextColor,
              fontSize: addedLine.labelTextSize,
              overflow: addedLine.textOverflow));

      textPaint.ellipsis =
          xAxis.textOverflow == TextOverflow.ellipsis ? kEllipsis : null;
      textPaint.textAlign = addedLine.labelAlignment.y == 0
          ? TextAlign.center
          : addedLine.labelAlignment.y < 0
              ? TextAlign.left
              : TextAlign.right;

      // stopY是顶部，startY是底部
      var textMaxWidth = startY - stopY;
      textPaint.layout(minWidth: textMaxWidth, maxWidth: textMaxWidth);
      var textHeight = textPaint.height;
      double offsetY;
      if (addedLine.labelAlignment.y > 0) {
        offsetY = stopY - textMaxWidth / 2 * (1 - addedLine.labelAlignment.y);
      } else if (addedLine.labelAlignment.y < 0) {
        offsetY = stopY + textMaxWidth / 2 * (addedLine.labelAlignment.y + 1);
      } else {
        offsetY = stopY;
      }

      canvas.save();
      canvas.rotate(pi / 2);
      double offsetX =
          x + textHeight / 2 + addedLine.labelAlignment.x * textHeight / 2;

      textPaint.paint(
        canvas,
        Offset(offsetY, -offsetX),
      );
      canvas.restore();
    }

    canvas.restore();
    textPaint.dispose();
  }
}

/// 折线图高亮数据绘制器
class LineChartHighLightRender {
  const LineChartHighLightRender({
    this.lineColor = Colors.green,
    this.lineWidth = 1,
    this.lineDash,
    this.fontColor = Colors.black,
    this.fontSize = 16,
  });

  final Color lineColor;
  final double lineWidth;

  /// 绘制虚线时，指定虚实之间的长度。设置此值则高亮线为虚线，否则为实线。
  final List<double>? lineDash;
  final Color fontColor;
  final double fontSize;

  /// 绘制高亮内容，此时[LineEntity.drawRect]已就绪
  ///
  /// [canvas] 画布
  ///
  /// [rect] 水平可绘制区域。
  ///
  /// rect.left:起始点坐标 = x轴起始点 + [XAxis.startPadding]。
  ///
  /// rect.top:可绘制区域顶部坐标 = y轴顶部 - [YAxis.endPadding]。
  ///
  /// rect.right:水平可绘制区域结束点坐标 = x轴结束点 - [XAxis.endPadding]。
  ///
  /// rect.bottom:可绘制区域底部坐标 = y轴底部 + [YAxis.startPadding]。
  void onDraw(KqLineChartDelegate chart, Canvas canvas, Rect rect,
      List<LineEntity> data) {
    var tempPath = Path();
    var paint = Paint();
    var textPaint = TextPainter(textDirection: TextDirection.ltr);

    paint.color = lineColor;
    paint.style = PaintingStyle.stroke;
    paint.strokeWidth = lineWidth;
    var textStyle = TextStyle(color: fontColor, fontSize: fontSize);

    for (var it in data) {
      tempPath.reset();
      tempPath.moveTo(rect.left, it.drawRect.top);
      tempPath.lineTo(it.drawRect.left, it.drawRect.top);
      if (lineDash != null) {
        tempPath = tempPath.dashPath(lineDash!);
      }
      canvas.drawPath(tempPath, paint);

      tempPath.reset();
      tempPath.moveTo(it.drawRect.left, rect.bottom);
      tempPath.lineTo(it.drawRect.left, it.drawRect.top);
      if (lineDash != null) {
        tempPath = tempPath.dashPath(lineDash!);
      }
      canvas.drawPath(tempPath, paint);

      var label = it.label;
      if (label == null || label.isEmpty) {
        continue;
      }

      textPaint.text = TextSpan(text: label, style: textStyle);
      textPaint.layout();

      var textWidth = textPaint.width;
      var textHeight = textPaint.height;
      var maxWidth = rect.right - rect.left;
      double drawX;
      if (maxWidth < textWidth) {
        drawX = rect.left;
      } else {
        maxWidth = textWidth;
        var cx = it.drawRect.center.dx;
        if (cx - rect.left >= textWidth / 2) {
          if (cx + textWidth / 2 > rect.right) {
            drawX = rect.right - textWidth;
          } else {
            drawX = cx - textWidth / 2;
          }
        } else if (cx + textWidth / 2 > rect.right) {
          drawX = rect.right - textWidth;
        } else {
          drawX = rect.left;
        }
      }

      double drawY = max(it.drawRect.top - textHeight, rect.top);
      textPaint.paint(canvas, Offset(drawX, drawY));
    }

    textPaint.dispose();
  }
}

/// 折线图手势处理器
class LineCharGestureHandler
    extends BaseAxisChartGestureHandler<KqLineChartDelegate, List<LineEntity>> {
  LineCharGestureHandler(
      {super.tapEnable, super.dragEnable, super.onTap, super.touchData});

  @override
  List<LineEntity>? getTouchData(Offset offset, bool isMove) {
    var chart = super.chart;
    if (chart == null) {
      return null;
    }

    var data = chart.data;
    if (data == null || data.isEmpty) {
      return null;
    }

    var list = <LineEntity>[];
    for (var entity in data) {
      for (var value in entity.values) {
        if (_isTouchInRectF(offset, value.drawRect, isMove)) {
          list.add(value);
          break;
        }
      }
    }
    return list.isEmpty ? null : list;
  }

  /// 点击是否在指定范围内容，[isYAllValid]表示是否y轴全局域触摸有效，如果为true，那么则无需判断手指y轴坐标
  bool _isTouchInRectF(Offset offset, Rect rect, bool isYAllValid) {
    var validRange = 10;
    if (offset.dx >= rect.left - validRange &&
        offset.dx <= rect.left + validRange) {
      if (isYAllValid) {
        return true;
      }

      return offset.dy >= rect.top - validRange &&
          offset.dy <= rect.top + validRange;
    }

    return false;
  }
}
